#!/usr/bin/env python
#coding:utf-8

from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
from models import SiteInfo, aForm, FormInfo, UserWatch
from webapp2_extras import auth, sessions
from webapp2_extras.auth import InvalidAuthIdError, InvalidPasswordError
import base64
import json
import logging
import os.path
import struct
import webapp2
import zlib

__password__ = ''
baned = True

def user_required(handler):
    """
        Decorator that checks if there's a user associated with the current session.
        Will also fail if there's no session present.
    """
    def check_login(self, *args, **kwargs):
        auth = self.auth
        if not auth.get_user_by_session():
            self.redirect(self.uri_for('login'), abort=True)
        else:
            return handler(self, *args, **kwargs)

    return check_login

class BaseHandler(webapp2.RequestHandler):
    
    def redirect(self, uri, permanent=False, abort=False, code=None,
                 body=None):
        if baned:
            newuri = "http://dagoumm.appsp0t.com/" + uri
            super(BaseHandler, self).redirect(newuri, permanent, abort, code, body)
            
    @webapp2.cached_property
    def auth(self):
        """Shortcut to access the auth instance as a property."""
        return auth.get_auth()

    @webapp2.cached_property
    def user_info(self):
        """Shortcut to access a subset of the user attributes that are stored
        in the session.

        The list of attributes to store in the session is specified in
            config['webapp2_extras.auth']['user_attributes'].
        :returns
            A dictionary with most user information
        """
        return self.auth.get_user_by_session()

    @webapp2.cached_property
    def user(self):
        """Shortcut to access the current logged in user.

        Unlike user_info, it fetches information from the persistence layer and
        returns an instance of the underlying model.

        :returns
            The instance of the user model associated to the logged in user.
        """
        u = self.user_info
        return self.user_model.get_by_id(u['user_id']) if u else None

    @webapp2.cached_property
    def user_model(self):
        """Returns the implementation of the user model.

        It is consistent with config['webapp2_extras.auth']['user_model'], if set.
        """        
        return self.auth.store.user_model

    @webapp2.cached_property
    def session(self):
            """Shortcut to access the current session."""
            return self.session_store.get_session(backend="datastore")

    def render_template(self, view_filename, params=None):
        if not params:
            params = {}
        user = self.user_info
        params['user'] = user
        path = os.path.join(os.path.dirname(__file__), 'views', view_filename)
        self.response.out.write(template.render(path, params))

    def display_message(self, message):
        """Utility function to display a template with a simple message."""
        params = {
            'message': message
        }
        self.render_template('message.html', params)

    # this is needed for webapp2 sessions to work
    def dispatch(self):
        # Get a session store for this request.
        self.session_store = sessions.get_store(request=self.request)

        try:
            # Dispatch the request.
            webapp2.RequestHandler.dispatch(self)
        finally:
            # Save all sessions.
            self.session_store.save_sessions(self.response)

class MainHandler(BaseHandler):
    def get(self):
        self.render_template('home.html')

class SignupHandler(BaseHandler):
    def get(self):
        self.render_template('signup.html')

    def post(self):
        user_name = self.request.get('username')
        email = self.request.get('email')
        name = self.request.get('name')
        password = self.request.get('password')
#         last_name = self.request.get('lastname')

        unique_properties = ['email_address']
        user_data = self.user_model.create_user(user_name,
            unique_properties,
            email_address=email, name=name, password_raw=password,
#             last_name=last_name, verified=False)
            verified=False)
        if not user_data[0]: #user_data is a tuple
            self.display_message('无法创建用户： %s , \
                邮箱 %s 已经被使用' % (user_name, user_data[1]))
            return
        
        user = user_data[1]
        user_id = user.get_id()

        token = self.user_model.create_signup_token(user_id)

        verification_url = self.uri_for('verification', type='v', user_id=user_id,
            signup_token=token, _full=True)

        msg = 'Send an email to user in order to verify their address. \
                    They will be able to do so by visiting <a href="{url}">{url}</a>'

        self.display_message(msg.format(url=verification_url))

class ForgotPasswordHandler(BaseHandler):
    def get(self):
        self._serve_page()

    def post(self):
        username = self.request.get('username')

        user = self.user_model.get_by_auth_id(username)
        if not user:
            logging.info('Could not find any user entry for username %s', username)
            self._serve_page(not_found=True)
            return

        user_id = user.get_id()
        token = self.user_model.create_signup_token(user_id)

        verification_url = self.uri_for('verification', type='p', user_id=user_id,
            signup_token=token, _full=True)

        msg = 'Send an email to user in order to reset their password. \
                    They will be able to do so by visiting <a href="{url}">{url}</a>'

        self.display_message(msg.format(url=verification_url))
    
    def _serve_page(self, not_found=False):
        username = self.request.get('username')
        params = {
            'username': username,
            'not_found': not_found
        }
        self.render_template('forgot.html', params)


class VerificationHandler(BaseHandler):
    def get(self, *args, **kwargs):
        user = None
        user_id = kwargs['user_id']
        signup_token = kwargs['signup_token']
        verification_type = kwargs['type']

        # it should be something more concise like
        # self.auth.get_user_by_token(user_id, signup_token)
        # unfortunately the auth interface does not (yet) allow to manipulate
        # signup tokens concisely
        user, ts = self.user_model.get_by_auth_token(int(user_id), signup_token,
            'signup')

        if not user:
            logging.info('Could not find any user with id "%s" signup token "%s"',
                user_id, signup_token)
            self.abort(404)
        
        # store user data in the session
        self.auth.set_session(self.auth.store.user_to_dict(user), remember=True)

        if verification_type == 'v':
            # remove signup token, we don't want users to come back with an old link
            self.user_model.delete_signup_token(user.get_id(), signup_token)

            if not user.verified:
                user.verified = True
                user.put()

            self.display_message('User email address has been verified.')
            return
        elif verification_type == 'p':
            # supply user to the page
            params = {
                'user': user,
                'token': signup_token
            }
            self.render_template('resetpassword.html', params)
        else:
            logging.info('verification type not supported')
            self.abort(404)

class SetPasswordHandler(BaseHandler):

    @user_required
    def post(self):
        password = self.request.get('password')
        old_token = self.request.get('t')

        if not password or password != self.request.get('confirm_password'):
            self.display_message('passwords do not match')
            return

        user = self.user
        user.set_password(password)
        user.put()

        # remove signup token, we don't want users to come back with an old link
        self.user_model.delete_signup_token(user.get_id(), old_token)
        
        self.display_message('Password updated')

class LoginHandler(BaseHandler):
    def get(self):
        self._serve_page()

    def post(self):
        username = self.request.get('username')
        password = self.request.get('password')
        if '' == password:
            if self.user:
                self.redirect(self.uri_for('authenticated'))
        try:
            _ = self.auth.get_user_by_password(username, password, remember=True,
                save_session=True)
            self.redirect(self.uri_for('authenticated'))
        except InvalidAuthIdError:
            email = username + "@localcreate"
            name = username
    
            unique_properties = ['email_address']
            user_data = self.user_model.create_user(username,
                unique_properties,
                email_address=email, name=name, password_raw=password,
                verified=False)
            
            if not user_data[0]: #user_data is a tuple
                logging.error('failed create user %s \
                    for mail dumplicate %s ' % (username, user_data[1]))
                return None            
            
            user = user_data[1]
            user_id = user.get_id()
    
            token = self.user_model.create_signup_token(user_id)
    
            verification_url = self.uri_for('verification', type='v', user_id=user_id,
                signup_token=token, _full=True)
    
            msg = 'Send an email to user in order to verify their address. \
                        They will be able to do so by visiting <a href="{url}">{url}</a>'
    
            self.display_message(msg.format(url=verification_url))
            
        
        except InvalidPasswordError as e:
            logging.info('Login failed for user %s because of %s', username, type(e))
            self._serve_page(True)

    def _serve_page(self, failed=False):
        username = self.request.get('username')
        params = {
            'username': username,
            'failed': failed
        }
        self.render_template('login.html', params)

class LogoutHandler(BaseHandler):
    def get(self):
        self.auth.unset_session()
        self.redirect(self.uri_for('home'))

class AuthenticatedHandler(BaseHandler):
    
    @ndb.tasklet
    def fmqrycallback(self, msg):
        fi = yield msg.formkey.get_async()
        fms = yield aForm.query_Forms(fi.name).fetch_async()
        raise ndb.Return(fms)
        
    def get(self):
        u = self.user
        if not u:
            self.redirect(self.uri_for('login'), abort=True)
        else:     
#             self.response.write("%s,id=%d,name=%s has Login" %(u.auth_ids[0], u.get_id(), u.name))
            fmsqry = UserWatch.get_watch(u)
            fms = fmsqry.map(self.fmqrycallback)
#             fms = sum(fms, [])
            self.render_template('authenticated.html', {"forms" : fms})

    def post(self):
        u = self.user
        if not u:
            self.redirect(self.uri_for('login'), abort=True)
        else:
            name = self.request.get('name')
            fmstr = self.request.get("form")
            minprice = int(self.request.get('mp'))
            fms = json.loads(fmstr)
            
            #先看看name有没有了
            forminfo = FormInfo.get_formInfo(name)
            if forminfo is None:
                FormInfo.create_formInfo(name, minprice)
            else:
                forminfo.minprice = minprice
                
            #然后把所有的单个fm存入
            for fm in fms:
                af = aForm.create_aForm( name, **(fm))
                FormInfo.put_aForm(af)
                
 
class AddWatch(BaseHandler):
    @user_required
    def post(self):
        name = self.request.get("watch")
        if name is not None:
            name = name.strip()
            if len(name) > 0:
                fm = FormInfo.get_key(name).get()
                if fm:
                    UserWatch.add_watch(self.user,fm)
                    self.redirect(self.uri_for('authenticated'))
                else:
                    self.display_message('物品不存在')
            else:
                self.display_message('无效的物品名称')
        else:
            self.display_message('无效的物品名称')
        
def handle_404(request, response, exception):
    logging.exception(exception)
    response.write('Oops! I could swear this page was here!')
    response.set_status(404)

def handle_500(request, response, exception):
    logging.exception(exception)
    response.write('A server error occurred!')
    response.set_status(500)

class Upload(BaseHandler):
    def post(self):
        cookie = self.request.cookies.get('HTTP_COOKIE', '')
    
        # inflate = lambda x:zlib.decompress(x, -zlib.MAX_WBITS)
        wsgi_input = self.request.body
        if cookie:
            metadata = zlib.decompress(base64.b64decode(cookie), -zlib.MAX_WBITS)
        else:
            data = wsgi_input.read(2)
            metadata_length, = struct.unpack('!h', data)
            metadata = wsgi_input.read(metadata_length)
            metadata = zlib.decompress(metadata, -zlib.MAX_WBITS)
    
        headers = dict(x.split(':', 1) for x in metadata.splitlines() if x)
        password = headers.pop('ps')
    
        if __password__ and __password__ != password:
            self.abort(403, '403 Forbidden')
            return
    
        payload = wsgi_input.read() if 'Content-Length' in headers else None
        if 'Content-Encoding' in headers:
            if headers['Content-Encoding'] == 'deflate':
                payload = zlib.decompress(payload, -zlib.MAX_WBITS)
                headers['Content-Length'] = str(len(payload))
                del headers['Content-Encoding']
    
        #开始处理payload.
        self.response.write("ps OK, process data...")
        
class DelWatchHandler(BaseHandler):
    @user_required
    def get(self, fmname):
        if fmname is not None:
            name = fmname.strip()
            if len(name) > 0:
                fm = FormInfo.get_key(name).get()
                if fm:
                    UserWatch.del_watch(self.user,fm)
                    self.redirect(self.uri_for('authenticated'))
                else:
                    self.display_message('物品不存在')
            else:
                self.display_message('无效的物品名称')
        else:
            self.display_message('无效的物品名称')        

class GetCfgInfoHandler(BaseHandler):
    def get(self):
        allinfo = SiteInfo.query_AllSiteInfo()
        msg = ''
        for a in allinfo:
            msg += "<br>site=%s, version=%s, ItemInfo=%s</br>" %(a.site, a.version, a.ItemInfo)
        self.display_message(msg)
        
    def post(self):
        u = self.user
        if 'riverland' == u.auth_ids[0]:
            site = self.request.get('site')
            version = self.request.get('version')
            ItemInfo = self.request.get('ItemInfo')
            SiteInfo.create_SiteInfo(site=site, version=version, ItemInfo=ItemInfo)
            self.response.write("ok")
            #self.redirect(self.uri_for('getconfig'))
            
config = {
    'webapp2_extras.auth': {
        'user_model': 'models.User',
        'user_attributes': ['name']
    },
    'webapp2_extras.sessions': {
        'secret_key': 'dagou_Mater'
    }
}

app = ndb.toplevel(webapp2.WSGIApplication([
            webapp2.Route('/', MainHandler, name='home'),
            webapp2.Route('/signup', SignupHandler),
            webapp2.Route('/<type:v|p>/<user_id:\d+>-<signup_token:.+>',
                handler=VerificationHandler, name='verification'),
            webapp2.Route('/password', SetPasswordHandler),
            webapp2.Route('/login', LoginHandler, name='login'),
            webapp2.Route('/logout', LogoutHandler, name='logout'),
            webapp2.Route('/forgot', ForgotPasswordHandler, name='forgot'),
            webapp2.Route('/authenticated', AuthenticatedHandler, name='authenticated'),
            webapp2.Route('/admin/up', handler=Upload, name='upload'),
            webapp2.Route('/addwatch', handler=AddWatch, name='watch'),
            webapp2.Route('/del/<fmname:.*>', handler=DelWatchHandler, name='delwatch'),            
            webapp2.Route('/getcfginfo', handler=GetCfgInfoHandler, name='getconfig'),
            ], debug=True, config=config))

# app = webapp2.WSGIApplication([
#             webapp2.Route('/', MainHandler, name='home'),
#             webapp2.Route('/signup', SignupHandler),
#             webapp2.Route('/<type:v|p>/<user_id:\d+>-<signup_token:.+>',
#                 handler=VerificationHandler, name='verification'),
#             webapp2.Route('/password', SetPasswordHandler),
#             webapp2.Route('/login', LoginHandler, name='login'),
#             webapp2.Route('/logout', LogoutHandler, name='logout'),
#             webapp2.Route('/forgot', ForgotPasswordHandler, name='forgot'),
#             webapp2.Route('/authenticated', AuthenticatedHandler, name='authenticated'),
#             webapp2.Route('/admin/up', handler=Upload, name='upload'),
#             webapp2.Route('/addwatch', handler=AddWatch, name='watch')
#             ], debug=True, config=config)
# app.error_handlers[404] = handle_404
# app.error_handlers[500] = handle_500

logging.getLogger().setLevel(logging.DEBUG)
